package com.yugao.fintech.draper.tenant.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import com.yugao.fintech.draper.auth.api.TenantFeignApi;
import com.yugao.fintech.draper.datasource.utils.MyBatisUtils;
import com.yugao.fintech.draper.message.queue.client.RedisMQTemplate;
import com.yugao.fintech.draper.multicache.properties.MultiCacheProperties;
import com.yugao.fintech.draper.tenant.aspect.IgnoreTenantAspect;
import com.yugao.fintech.draper.tenant.cache.TenantCacheManager;
import com.yugao.fintech.draper.tenant.cache.TenantRedisCaffeineCacheManager;
import com.yugao.fintech.draper.tenant.context.TenantContextWebFilter;
import com.yugao.fintech.draper.tenant.context.TenantRequestInterceptor;
import com.yugao.fintech.draper.tenant.handler.TenantDatabaseHandler;
import com.yugao.fintech.draper.tenant.security.TenantSecurityWebFilter;
import com.yugao.fintech.draper.tenant.service.TenantValidService;
import feign.RequestInterceptor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;

import java.util.Objects;

/**
 * 多租户自动配置类
 */
@AutoConfiguration
@ConditionalOnProperty(prefix = "draper.tenant", value = "enable", matchIfMissing = true)
@EnableConfigurationProperties(TenantProperties.class)
public class TenantAutoConfiguration {
    /**
     * Mybytis-plus多租户拦截器
     *
     * @param properties  多租户配置
     * @param interceptor Mybatis-plus拦截器
     * @return Mybatis-plus多租户拦截器
     */
    @Bean
    public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties properties,
                                                                 MybatisPlusInterceptor interceptor) {
        // Mybatis-plus多租户拦截器
        TenantLineInnerInterceptor inner = new TenantLineInnerInterceptor(new TenantDatabaseHandler(properties));
        // 添加拦截器到首个位置 保证在分页插件前执行
        MyBatisUtils.addInterceptor(interceptor, inner, 0);
        return inner;
    }

    /**
     * 多租户上下文过滤器
     */
    @Bean
    public FilterRegistrationBean<TenantContextWebFilter> tenantContextWebFilter() {
        FilterRegistrationBean<TenantContextWebFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new TenantContextWebFilter());
        registrationBean.setOrder(-110);
        return registrationBean;
    }

    /**
     * 租户合法校验过滤器
     */
    @Bean
    public FilterRegistrationBean<TenantSecurityWebFilter> tenantSecurityWebFilter(TenantProperties tenantProperties,
                                                                                   TenantValidService tenantValidService) {
        FilterRegistrationBean<TenantSecurityWebFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new TenantSecurityWebFilter(tenantProperties, tenantValidService));
        registrationBean.setOrder(SecurityProperties.DEFAULT_FILTER_ORDER + 1);
        return registrationBean;
    }

    /**
     * 传递Feign租户请求头
     */
    @Bean
    public RequestInterceptor feignTenantInterceptor() {
        return new TenantRequestInterceptor();
    }

    /**
     * 多级缓存支持多租户
     */
    @ConditionalOnProperty(prefix = "redis.cache.multi", name = "enabled", havingValue = "true", matchIfMissing = true)
    @Bean
    @Primary
    public CacheManager cacheManager(MultiCacheProperties multiCacheProperties,
                                     RedisTemplate redisTemplate, RedisMQTemplate redisMQTemplate) {
        return new TenantRedisCaffeineCacheManager(multiCacheProperties, redisTemplate, redisMQTemplate);
    }

    /**
     * 多级缓存不开启时生效
     */
    @ConditionalOnProperty(prefix = "redis.cache.multi", name = "enabled", havingValue = "false", matchIfMissing = false)
    @Bean
    @Primary
    public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {
        RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory());
        RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration
                .defaultCacheConfig()
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()));
        return new TenantCacheManager(cacheWriter, redisCacheConfiguration);
    }

    /**
     * 多租户aop处理
     */
    @Bean
    public IgnoreTenantAspect ignoreTenantAspect() {
        return new IgnoreTenantAspect();
    }

    /**
     * 租户信息校验
     */
    @Bean
    public TenantValidService tenantValidService(TenantFeignApi tenantFeignApi) {
        return new TenantValidService(tenantFeignApi);
    }
}
